//***************************************************************************//
//*
//* 内容摘要：FEC编码接口
//*	
//* 当前版本：V1.0		
//* 作    者：zhfayuan
//* 完成日期：2016-8-21
//**************************************************************************//
#ifndef CSDFECENC_H
#define CSDFECENC_H

#include "SDCommDef.h"
#include "SDFecInterface.h"
#include "SDPacketBuffer.h"
#include "SDVanderEnc.h"
#include "SDSocket.h"
#include "SDQos.h"

typedef struct T_FecEncode
{
    T_FEC_DATA      tFecData[MAX_GROUP_SIZE];
	T_FEC_DATA      tFecParity[MAX_GROUP_SIZE];
    
    BOOL			bStartEnc;

    BOOL            bFirstPacket;     

    BOOL            bFirstPacketOfGroup; 

    UINT16          usLastParityPktLen;
    UINT16          uParityNum;
    UINT16          uRedundancy;
    UINT32          uiGroupPacketMaxLen;

    UINT8			uGroupNo;
    UINT8			uGroupSize;
    UINT8			uGroupPos;
    UINT8			uReserve;
}T_FecEncode;

#if ENABLE_NACK
//NACK请求包
#define NACK_CRC_CODE		0xFABC1234

typedef struct T_LocalSaveRtpPacket
{
	UINT    unCrcCode;		//有效性校验
	//RTP包时间戳
	UINT32  unTimestamp;
	//RTP包长度
	UINT32	unRtpPacketLen; 
	//RTP包序号
	UINT16	usSeq;
	//存放完整RTP包
	UINT8	ucRtpPacket[FEC_2K_SIZE];
}T_LocalSaveRtpPacket;


//本地最大缓存NACK请求数，来不及处理时丢弃早期请求
#define MAX_NACK_REQ_DEPTH	20

#define NACK_REQ_TYPE_DATA		0	//请求重发数据包
#define NACK_REQ_TYPE_IDR		1   //请求编码IDR帧

typedef struct T_NackRequest
{
	UINT			unCrcCode;		//有效性校验
	UINT			unUniqueId;		//标识本次请求的唯一ID，发送端会重复发送请求包，接收端需要根据此ID过滤重复包
	UINT			unNackReqType;  //NACK请求的类型，广义的NACK请求即包括重传数据包请求也包括IDR帧发送请求
	BOOL			bValid;
	unsigned short	usStartSeq;		//需要重传的起始序号
	unsigned short	usEndSeq;		//需要重传的结束序号
}T_NackRequest;
#endif

class CSDFecEncProcess
{
public:
	CSDFecEncProcess(unsigned int unLocalSaveRtpPacketsNum = 0)
	{
		FecEncode_CreateNew(unLocalSaveRtpPacketsNum);

		m_pvProtectMut = CSDMutex::CreateObject();
#if ENABLE_NACK
		m_pvProtectNackReq = CSDMutex::CreateObject();
#endif
	}
	virtual ~CSDFecEncProcess()
	{
		FecEncode_Destroy();
		CSDMutex::RealseObject(m_pvProtectMut);
		m_pvProtectMut = NULL;
#if ENABLE_NACK
		CSDMutex::RealseObject(m_pvProtectNackReq);
		m_pvProtectNackReq = NULL;
#endif
	}
	MD_STATUS FecEncode_Start();	
	MD_STATUS FecEncode_Stop();
	void FecEncode_Enable(bool bEnable = true);
	void FecEncode_setParams( UINT8 eFecParamType, void *value);	
	void FecEncode_getParams(UINT8 eFecParamType, void *value);	
	MD_STATUS FecEncode_encode(T_PacketBuffer *pPacketBuffer, USHORT &nSeqNum,
								UINT unNaluNum, UINT unNaluIndex);
#if ENABLE_NACK
	//收到远端的NACK请求
	void FecEncode_RemoteRequestRetrans(T_NackRequest *ptNackRequestFromRemote);
	//收到远端IDR帧请求
	virtual void pfRemoteRequestIdrCallback() {}
#endif

#if ENABLE_DYNAMIC_GROUP_SIZE
	void FecEncode_EnableDynamicGroup(BOOL bEnable) {m_bEnableDynamicGroupSize = bEnable;}
#endif

private:
	void WaterMarkInit();

private:

	T_FecEncode *pFecEncode;
	CSDVanderEnc *pVanderEnc;
	unsigned char m_ucFecWaterMark[MAX_WATERMARK_LEN];
	unsigned int  m_uiFecWaterMarkLen;	

	void *m_pvProtectMut; 
	bool m_bEnableFecEnc;

	//当前使用的Group分组大小
	UINT8 m_uCurrGroupSize;

#if ENABLE_NACK
	//用于NACK机制时，本地RTP包备份
	T_LocalSaveRtpPacket *m_ptLocalRtpPackets;
	UINT m_unLocalSaveRtpPacketsNum;
	//下一个RTP包存放的位置
	UINT m_unLocalRtpSavePos;
	//NACK机制下，存放远端重传请求
	T_NackRequest *m_ptNackRequestFromRemote;
	//NACK机制下，由于发端同一个NACK会重复发送多次以避免丢包，收端需要进行过滤，保证只响应一次
	//将收到的NACK请求存入，若发现之前已有相同的请求则丢弃当前请求
	T_NackRequest *m_ptNackRequestBackup;
	//m_ptNackRequestBackup中标记下一个NACK请求存放的位置
	UINT m_unNackBackupWritePos;

	//m_ptNackRequestFromRemote中标记下一个NACK请求存放的位置
	UINT m_unNackRequestWritePos;
	UINT m_unNackRequestReadPos;
	//保护m_ptNackRequestFromRemote的锁
	void *m_pvProtectNackReq;
	//当前待处理的NACK请求数
	UINT m_unNackRequestNum;
#endif

#if ENABLE_DYNAMIC_GROUP_SIZE
	//是否启用FEC动态Group大小
	BOOL m_bEnableDynamicGroupSize;
#endif

	void FecEncode_CreateNew(unsigned int unLocalSaveRtpPacketsNum);
	void FecEncode_Destroy(void);	
	MD_STATUS FecEncode_InitObject(void);
	void FecEncode_UnInitObject(void);
	void FecEncode_FreeBuffer();	

	virtual MD_STATUS pfFecEncOutCallback(T_PacketBuffer *pInPacketBuffer, T_PacketBuffer *pOutPacketBuffer) = 0;
#if ENABLE_NACK
	//NACK机制下缓存的RTP包应远端请求再次发送
	virtual MD_STATUS pfNackPacketOutCallback(UINT8 *pBuffer, UINT32 unLen) = 0;
#endif
	INT32 FecEncode_ParityDataOut(T_PacketBuffer *pPacketBuffer, USHORT &nSeqNum);	
	INT32 FecEncode_buildFecHeader(UINT8 *pBuffer, UINT32 uiParityAvgLen, UINT32 uiParityLastLen);	
};
#endif
